package com.beasts.ant.auth.config.oauth;

import com.alibaba.druid.pool.DruidDataSource;
import com.beasts.ant.auth.common.constant.CommonConstant;
import com.beasts.ant.auth.common.constant.SecurityConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证服务配置
 * <p>
 * ResourceOwnerPasswordTokenGranter ==> password密码模式
 * AuthorizationCodeTokenGranter ==> authorization_code授权码模式
 * ClientCredentialsTokenGranter ==> client_credentials客户端模式
 * ImplicitTokenGranter ==> implicit简化模式
 * RefreshTokenGranter ==>refresh_token 刷新token专用
 * </p>
 * @author Locker
 * @date 07/05/2018 9:54 AM
 * @since 1.0
 */
@Configuration
@Order(Integer.MIN_VALUE)
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthServerConfig authServerConfig;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private DruidDataSource dataSource;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 配置文件模式
        clients.inMemory()
                // 密码模式
                .withClient(authServerConfig.getClientId())
                .secret(authServerConfig.getClientSecret())
                .authorizedGrantTypes(SecurityConstants.REFRESH_TOKEN, SecurityConstants.PASSWORD)
                .scopes(authServerConfig.getScope())
                .accessTokenValiditySeconds(authServerConfig.getTokenValidityInSeconds())
                // 自动审批
                .autoApprove(true)
        ;

        // 数据库模式，多client并存，token分别产生
//        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
//        clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
//        clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
//        clients.withClientDetails(clientDetailsService);

    }

    /**
     * oauth 生成 token 定制化处理，使用TokenEnhancer来修改授权服务器返回token的内容
     * @return
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        // jdk1.8
//        return (accessToken, authentication) -> {
//            final Map<String, Object> additionInfo = new HashMap<>(1);
//            additionInfo.put("license", SecurityConstants.LICENSE);
//            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionInfo);
//            return accessToken;
//        };

        return new TokenEnhancer() {
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                if (accessToken instanceof DefaultOAuth2AccessToken) {
                    final Map<String, Object> additionInfo = new HashMap<>(1);

                    // 添加指定字段及信息到 token，仅限新生成的 token
//                    additionInfo.put("license", SecurityConstants.LICENSE);
//                    additionInfo.put("current_time", new Date());

                    // 将自定义信息加入默认 /oauth/token 返回值
                    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionInfo);
                }
                return accessToken;
            }
        };

    }

//    @Bean
//    public JwtAccessTokenConverter jwtAccessTokenConverter() {
//        SelfJwtAccessTokenConverter selfJwtAccessTokenConverter = new SelfJwtAccessTokenConverter();
//        selfJwtAccessTokenConverter.setSigningKey(CommonConstant.SIGN_KEY);
//        return selfJwtAccessTokenConverter;
//    }

    /**
     * tokenStore 定制化处理
     * @return TokenStore
     */
    @Bean
    public TokenStore tokenStore() {
        // token 存储到 redis
//        RedisTokenStore tokenStore = redisTokenStore();

        // token 存储到 db-mysql
        JdbcTokenStore tokenStore = jdbcTokenStore();

        return tokenStore;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // token 增强配置
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(
                Arrays.asList(
                        tokenEnhancer()
//                        , jwtAccessTokenConverter()
                )
        );
        endpoints.tokenStore(tokenStore())
                // token定制
                .tokenEnhancer(tokenEnhancerChain)
                // 认证管理器
                .authenticationManager(authenticationManager)
                // 启用刷新token功能
                .reuseRefreshTokens(true)
                // 绑定用户信息
                .userDetailsService(userDetailsService)
                // 允许 GET、POST 请求获取 token，默认只支持 POST，即访问端点：/oauth/token
                .allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
        ;

    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // 开启form表单认证
                .allowFormAuthenticationForClients()
                .tokenKeyAccess("isAuthenticated()")
                .checkTokenAccess("permitAll()")
        ;
    }

    @Bean
    public JdbcTokenStore jdbcTokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    @Bean
    public RedisTokenStore redisTokenStore() {
        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
        // TODO 加入自定义信息
        tokenStore.setPrefix(SecurityConstants.ANT_PREFIX);
        return tokenStore;
    }

}
